package org.skycastle.megastructure.space

import com.jme.math.Vector3f
import com.jme.scene.Node
import com.jme.system.DisplaySystem

/**
 * Contains a 3D environment.
 *
 * Supplies boundaries, environmental context, and physical simulation parameters.
 *
 * @author Hans Haggstrom
 */

abstract class Space {
  private var entities: List[Entity] = Nil
  private var display: DisplaySystem = null

  private var universe : Universe = null

  final def getUniverse = universe
  final def setUniverse ( uni : Universe) { universe = uni }

  /**
   * Updates physics and other things that need updating.
   */
  def update( timeSinceLastUpdate_s : Float )

  /**
   * Called during the render phase.
   */
  def render()

  /**
   *  The entities currently in the space
   */
  final def getEntities: List[Entity] = entities

  final def addEntity(entity: Entity) : Unit = addEntity( entity, new Vector3f() )

  final def addEntity(entity: Entity, position: Vector3f) {
    require(entity != null && !entities.contains(entity))


    if (entity.space != null) {
      entity.space.removeEntity( entity )
    }

    entities = entity :: entities

    entity.setSpace( this )
    entity.setPosition( position )

    onEntityAdded(entity, position)
  }

  final def removeEntity(entity: Entity) {
    require(entity != null)

    if (entities.contains(entity)) {
      entities = entities.remove(_ == entity)

      entity.setSpace( null )

      onEntityRemoved(entity)
    }
  }

  protected def onEntityAdded(entity: Entity, position: Vector3f)

  protected def onEntityRemoved(entity: Entity)

  private var scene: Node = null

  final def getSceneIfExists: Node = scene


  /**
   * The 3D scene for this space.
   */
  final def getScene(display: DisplaySystem): Node = {
    require(display != null)

    if (scene == null) {
      this.display = display
      scene = createScene(display)
    }

    scene
  }

  protected def createScene(display: DisplaySystem): Node

  protected final def getDisplay = display

}